home *** CD-ROM | disk | FTP | other *** search
/ SPACE 1 / SPACE - Library 1 - Volume 1.iso / program / 441 / sc_desk / batchmon / batchmon.c < prev    next >
C/C++ Source or Header  |  1990-11-23  |  34KB  |  985 lines

  1.  
  2. /**************************************************************************
  3.  *
  4.  * BATCHMON -  A simple batch monitor (non-interactive shell).
  5.  *             This program will read lines from a 'batch' or 'script'
  6.  *             file, invoke rudimentary variable subsitution on each line
  7.  *             read, then execute the command or program specified by the
  8.  *             line.  Certain basic commands (DELETE, WAIT, etc), are
  9.  *             implemented internally, but mostly this allows you to run
  10.  *             a set of programs in a given order, passing parms to those
  11.  *             programs.
  12.  *
  13.  * ??/??/??    v1.0 - This was crude indeed, and the source is long lost.
  14.  *
  15.  * 07/24/88    v2.0 - A total re-write, this one is pretty functional.
  16.  *             This version does more than just run programs, it includes
  17.  *             internal implementation of some functions, and support for
  18.  *             simple variable substitution. 
  19.  *
  20.  * 11/05/88    v2.1 - Recompiled with Sozobon, released as part of Sozobon.
  21.  *             This version can be compiled with Sozobon and dLibs; all 
  22.  *             routines linked from '\ianlibs\libi' are now included here  
  23.  *             in source form.  This version also includes more internal 
  24.  *             functions, especially path and envstring support.
  25.  *
  26.  *************************************************************************/
  27.  
  28. #include <osbind.h>
  29.  
  30. #define TRUE  1
  31. #define FALSE 0
  32.  
  33. #define MAXARGS 10
  34.  
  35. /* translate my lib function int2asc() into dlibs itoa() for sozobon      */
  36. /* translate dlibs bzero() function, because the dlibs version don't work */
  37.  
  38. #ifdef SOZOBON 
  39. #define int2asc(str,int)  itoa(int,str,10) 
  40. #define bzero(str,int)    myzero(str,int)
  41. #endif
  42.  
  43. char callbuf[128],
  44.      callparms[128],
  45.      *inpv[MAXARGS],
  46.      inputcmd[128],
  47.      inpfile[128],
  48.      callfile[128],
  49.      exepath[128] = "",
  50.      crlf_str[] = "\r\n";
  51.  
  52. int  inpc,
  53.      rcflag = TRUE;           /* Flag: Abort on non-zero RC? */
  54.  
  55. /*************************************************************************
  56.  *
  57.  * Main routine.
  58.  *  Process cmdline parms, or if none were received, prompt the user. Make
  59.  *  sure we can open/read the batch file, then for each line call the 
  60.  *  parser & command processor, continue until end-of-file or I/O error.
  61.  *
  62.  ************************************************************************/
  63.  
  64. main(argc, argv)
  65. int  argc;
  66. char *argv[];
  67. {
  68. int counter, linlen;
  69.  
  70. say("\033E\033vBATCHMON: Batch monitor v2.1\r\n\n");
  71.  
  72. if (argc > 1)
  73.      {
  74.      if (argc > MAXARGS)
  75.           {
  76.           bm_msg("Too many variables/arguments! (Max = 10).\r\n");
  77.           err_exit();
  78.           }
  79.      for (counter = 1; counter < argc; counter++)
  80.           inpv[counter-1] = argv[counter];
  81.      inpc = argc - 1;
  82.      }
  83. else
  84.      {
  85.      say("\033e\r\nEnter batch file name and parms (<CR> to exit)\r\n -> ");
  86.      getstr(inputcmd, 127, "");
  87.      inpc = bldargv(inpv, inputcmd, MAXARGS);
  88.      }
  89.      
  90. say("\033f");                      /* cursor off */
  91.  
  92. if (inpc == 0)                     /* if <CR>, terminate */
  93.      Pterm(0);
  94.  
  95. upcase(inpv[0]);                   /* force file/cmd name to uppercase */
  96. inpfile[0] = 0x00;                 /* start with null filename         */
  97. add_extension(inpfile, inpv[0], ".BAT");
  98.  
  99. linlen = getline(127, inpfile);    /* Open batch command file     */
  100.  
  101. if (linlen < 0)
  102.      {
  103.      bm_msg("Cannot open batch file '");
  104.      say(inpfile);
  105.      say("', status = ");
  106.      sayint(linlen);
  107.      err_exit();
  108.      }
  109.  
  110. do   {                             /* main loop: read file, exec cmds.    */
  111.      if (Bconstat(2))              /* if keystroke available...           */
  112.           check_abort();           /* see if user wants abort.            */
  113.      bzero(callbuf, sizeof(callbuf)); /* Start with clean line buffer.    */
  114.      linlen = getline(0, callbuf); /* Get the next line.                  */
  115.      if (linlen >= 0)              /* If not EOF or error...              */
  116.           if (do_substitution())   /* Parse & do variable substitution.   */
  117.                do_command();       /* If non-zero there is a cmd to exec. */
  118.      } while (linlen >= 0);        /* Loop until EOF or I/O error.        */
  119.  
  120. if (linlen != -128)
  121.      {
  122.      say(crlf_str);
  123.      bm_msg("Error occurred reading batch file '");
  124.      say(inpfile);
  125.      say("', status = ");
  126.      sayint(linlen);
  127.      err_exit();
  128.      }
  129.  
  130. Pterm(0);                          /* Normal exit, no wait-for-key */
  131.  
  132. }
  133.  
  134. /*************************************************************************
  135.  *
  136.  * Internal command modules.  These *have* to appear before the command
  137.  * table structure to compile properly on Alcyon.  Note when adding new
  138.  * routines that the following data structures are available to help them:
  139.  *  
  140.  *  callfile       - The name of the command, for what it's worth.
  141.  *  callparms[0]   - Byte count of string callparms[1-n].
  142.  *  callparms[1-n] - A string of characters which comprise the parms from
  143.  *                   the batch file after variable substitution. The string
  144.  *                   will be null-terminated, and if there were no parms 
  145.  *                   there will be a null char in callparms[1].
  146.  *
  147.  * Each routine should return an integer; zero if all went well, or an
  148.  * error code of some sort.  Right now this makes no difference, but a
  149.  * future version might/will report these return codes to the user and/or
  150.  * treat them in general the same as an RC from an external program.
  151.  ************************************************************************/
  152.  
  153. /*-------------------------------------------------------------------------
  154.  * DEL/DELETE - Delete 1-10 files.
  155.  *-----------------------------------------------------------------------*/
  156.  
  157. cmd_delete()
  158. {
  159. char *delv[10];
  160. int  delc,
  161.      counter,
  162.      status;
  163.  
  164. delc = bldargv(delv, &callparms[1], 10);     /* build filename list     */
  165.      
  166. if (delc)                                    /* if anything to delete...*/
  167.      {
  168.      for (counter = 0; counter < delc; counter++)
  169.           {
  170.           status = Fdelete(delv[counter]);   
  171.           if (status)
  172.                bm_msg("Cannot delete file ");
  173.           else
  174.                bm_msg("Deleted file ");
  175.           say(delv[counter]);
  176.           say(crlf_str);
  177.           }
  178.      }
  179. else
  180.      {
  181.      bm_msg("No files to delete!");
  182.      say(crlf_str);
  183.      }
  184.      
  185. return(0);
  186. }
  187.  
  188. /*-------------------------------------------------------------------------
  189.  * COM/REM - Do nothing.
  190.  *-----------------------------------------------------------------------*/
  191.  
  192. cmd_null()
  193. {
  194. return(0);
  195. }
  196.  
  197. /*-------------------------------------------------------------------------
  198.  * WAIT/PAUSE - Stop and prompt for a keystroke to continue.
  199.  *-----------------------------------------------------------------------*/
  200.  
  201. cmd_wait()
  202. {
  203. int workchr;
  204.  
  205. bm_msg("Hit any key to continue...\r\n");
  206.  
  207. do   {
  208.      workchr = Bconin(2);     /* Another piece of artificial stupidity:   */
  209.      switch (workchr)         /* When waiting for a keystroke, kill       */
  210.           {                   /* the program on a ^C, and ignore ^S & ^Q. */
  211.           case 0x03:          
  212.                Pterm(0);      
  213.           case 0x11:          
  214.           case 0x13:
  215.                break;
  216.           default:
  217.                workchr = 0x00;
  218.           }
  219.      } while (workchr);
  220.      
  221. return(0);
  222. }
  223.  
  224. /*-------------------------------------------------------------------------
  225.  * RCSTOP - Set the flag to stop on a non-zero return code.
  226.  *-----------------------------------------------------------------------*/
  227.  
  228. cmd_rcstop()
  229. {
  230. rcflag = TRUE;
  231. return(0);
  232. }
  233.  
  234. /*-------------------------------------------------------------------------
  235.  * NORCSTOP - Set the flag to cruise through a non-zero return code.
  236.  *-----------------------------------------------------------------------*/
  237.  
  238. cmd_norcstop()
  239. {
  240. rcflag = FALSE;
  241. return(0);
  242. }
  243.  
  244. /*-------------------------------------------------------------------------
  245.  * SETENV - Do something to the environment. Something = whatever is on
  246.  *  the command line.
  247.  *-----------------------------------------------------------------------*/
  248.  
  249. cmd_setenv()
  250. {
  251. putenv(&callparms[1]);
  252. return(0);
  253. }
  254.  
  255. /*-------------------------------------------------------------------------
  256.  * EXEPATH - Set path for programs called from the batch monitor.
  257.  *-----------------------------------------------------------------------*/
  258.  
  259. cmd_exepath()
  260. {
  261. fixpath(&callparms[1],TRUE);       /* Force path to end in '\'. */
  262. strcpy(exepath, &callparms[1]);    /* Set new default path for exe files */
  263. return(0);
  264. }
  265.  
  266. /*-------------------------------------------------------------------------
  267.  * DEFPATH - Set path for programs called from the batch monitor.
  268.  *-----------------------------------------------------------------------*/
  269.  
  270. cmd_defpath()
  271. {
  272. int status;
  273.  
  274. fixpath(&callparms[1],FALSE);            /* Force path to not end in '\'. */
  275.  
  276. upcase(&callparms[1]);
  277.  
  278. if (callparms[2] == ':')
  279.      {
  280.      Dsetdrv(callparms[1] - 'A');
  281.      status = Dsetpath(&callparms[3]); 
  282.      }
  283. else
  284.      status = Dsetpath(&callparms[1]);
  285.      
  286. if (status)
  287.      {
  288.      bm_msg("DOS status ");
  289.      sayint(status);
  290.      say(" setting path ");
  291.      say(&callparms[1]);
  292.      say(crlf_str);
  293.      }
  294. return(status);
  295. }
  296.  
  297. /*-------------------------------------------------------------------------
  298.  *
  299.  *-----------------------------------------------------------------------*/
  300.  
  301. /**************************************************************************
  302.  *
  303.  * Command table structure.  Maps command names to the functions that
  304.  * handle the command. This is serious overkill for this simple batch
  305.  * monitor, but it will make for simpler extension in the future.
  306.  *
  307.  *************************************************************************/
  308.  
  309. struct {
  310.      int  (*cmd_ptr)();       /* pointer to function that returns int */
  311.      char *cmdname;           /* pointer to command name              */
  312.        } command_table[] = {
  313.      cmd_delete,    "DEL",
  314.      cmd_delete,    "DELETE",
  315.      cmd_wait,      "WAIT",
  316.      cmd_wait,      "PAUSE",
  317.      cmd_null,      "COM",
  318.      cmd_null,      "REM",
  319.      cmd_rcstop,    "RCSTOP",
  320.      cmd_norcstop,  "NORCSTOP",
  321.      cmd_exepath,   "EXEPATH",
  322.      cmd_defpath,   "DEFPATH",
  323.      cmd_setenv,    "SETENV"
  324.        };
  325.  
  326. #define NUMCMDS 11
  327.  
  328. /**************************************************************************
  329.  *
  330.  * do_command - Decide whether command is 1) a comment, 2) internal, or
  331.  *              3) external, and take the appropriate action. (Ignore it,
  332.  *              call the internal routine, or call the external program).
  333.  *
  334.  *************************************************************************/
  335.  
  336. #define COMMENT1 '*'
  337. #define COMMENT2 '!'
  338. #define COMMENT3 ';'
  339.  
  340. do_command()
  341. {
  342. register char workchr;
  343.  
  344. char          workname[128];
  345.  
  346. register int  counter,
  347.               status;
  348.  
  349. long          execstat;
  350.  
  351. /*-----------------------------------------------------------------------
  352.  * If the first character is a comment flag, ignore the entire line.
  353.  *-----------------------------------------------------------------------*/
  354.   
  355. workchr = callfile[0];
  356. if ((workchr == COMMENT1) || (workchr == COMMENT2) || (workchr == COMMENT3))
  357.      return(0);
  358.  
  359. /*-----------------------------------------------------------------------
  360.  *  Force the command/filename to uppercase, and compare it to each 
  361.  *  entry in the command table.  If a match is found, call the 
  362.  *  corresponding internal command processor, return that processor's
  363.  *  return code to our caller.
  364.  *-----------------------------------------------------------------------*/
  365.    
  366. upcase(callfile); 
  367. for (counter = 0; counter < NUMCMDS; counter++) 
  368.      if (0 == strcmp(callfile, command_table[counter].cmdname))
  369.           return( (*(command_table[counter].cmd_ptr))() );           
  370.      
  371. /*-----------------------------------------------------------------------
  372.  * It's not an internal command, so try to call it as a program.
  373.  *-----------------------------------------------------------------------*/
  374.  
  375. strcpy(workname, exepath);
  376. add_extension(workname, callfile, ".PRG");
  377. status = execstat = Pexec(0, workname, callparms, 0L);
  378.  
  379. if (execstat < 0L)                 /* If DOS error... */
  380.      {
  381.      bm_msg("Error occurred attempting to run '");
  382.      say(workname);
  383.      say("', status = ");
  384.      sayint(status);
  385.      say(crlf_str);
  386.      }
  387. else
  388.      {
  389.      if (status)                   /* If program returned non-zero... */
  390.           {
  391.           bm_msg("Program exited with status code ");
  392.           sayint(status);
  393.           say(crlf_str);
  394.           if (rcflag)
  395.                check_abort();
  396.           }
  397.      }
  398.  
  399. return((int)execstat);
  400. }
  401.  
  402. /**************************************************************************
  403.  *
  404.  * Routine to parse a line from the batch file and perform variable
  405.  * substitution.  Pure ugly brute force coding can be found here.
  406.  *
  407.  *************************************************************************/
  408.  
  409. do_substitution()
  410. {
  411. register char *p_rawparm,     /* -> curloc in raw input from batch file   */
  412.               *p_parsdparm,   /* -> curloc in parsed cmdline we're building */
  413.               *p_subsparm,    /* -> curloc in a substitution variable     */
  414.               workchr;        /* what do you *think* this is ?            */
  415.               
  416. register int  counter,        /* Count of bytes in parsed cmdline         */
  417.               workidx;        /* used when doing variable substitution    */
  418.  
  419. char          *callv[2];      /* Pointers to first word & rest of words   */
  420. int           callc;          /* in raw image, and count of pointers.     */
  421.  
  422. static   int  msgissued[10];  /* Keep track of var subs err msgs issued.  */
  423.  
  424. /*-----------------------------------------------------------------------
  425.  * Parse command line into 2 pieces and decide what to do with it...
  426.  *-----------------------------------------------------------------------*/
  427.  
  428. callc = bldargv(callv, callbuf, 2);   /* Separate 1st word from the rest  */
  429.  
  430. switch (callc)                /* What did the input line look like?       */
  431.      {
  432.      case 0:
  433.           say(crlf_str);      /* An empty input line... just ouput an     */
  434.           return(0);          /* empty line, and return a zero.           */
  435.           break;
  436.      case 1:
  437.           callv[1] = "";      /* Command/prog name w/no parms, force null */
  438.           break;
  439.      default:                 /* Else command & parms, all set to go.     */
  440.           break;
  441.      }
  442.  
  443. p_rawparm = callv[1];         /* Prime pointer to raw input image.        */
  444. p_parsdparm = &callparms[1];  /* Prime pointer to final cmdline image.    */
  445. counter = 0;                  /* Assume cmdline length of zero.           */
  446.  
  447. /*-----------------------------------------------------------------------
  448.  * Main loop to process variable substitution into command line image...
  449.  *-----------------------------------------------------------------------*/
  450.  
  451. do   {                                            /* Loop for var subs...  */           
  452.  
  453.      workchr = *p_parsdparm = *p_rawparm;         /* Get/move a character  */
  454.  
  455.      if (workchr == '%')                          /* If char is not '%'    */
  456.           {                                       /* skip to end of loop.  */
  457.           workidx = *(p_rawparm+1) - '0';         /* Turn next char into an*/
  458.           if ((workidx >= 0) && (workidx < inpc)) /* index (%1-%9 = 1-9),  */
  459.                {                                  /* range check against   */
  460.                p_rawparm += 2;                    /* # of args available.  */
  461.                p_subsparm = inpv[workidx];        /* If valid, incr the    */
  462.                while (*p_subsparm)                /* rawparm ptr (suck up  */
  463.                     {                             /* the index char), and  */
  464.                     *p_parsdparm++ = *p_subsparm++;/* copy the subs value  */
  465.                     counter++;                    /* to the final string.  */
  466.                     }                             /* If numeric but not    */
  467.                } /* END var in range */           /* valid, incr the raw   */
  468.           else                                    /* ptr but not the parsed*/
  469.                {                                  /* so that we effectively*/
  470.                if ((workidx >= 0) && (workidx <= 9)) /* make the missing   */
  471.                     {                             /* var just disappear.   */
  472.                     if (!msgissued[workidx])      /* Issue a warning the   */
  473.                          {                        /* 1st time we do this.  */
  474.                          bm_msg("Warning...No runtime value supplied for variable %");
  475.                          Bconout(2,'0' + workidx);
  476.                          say(crlf_str);
  477.                          msgissued[workidx] = TRUE;
  478.                          }
  479.                     p_rawparm += 2;     /* For a '%' followed by a digit   */
  480.                     }                   /* out of range, incr raw ptr past */
  481.                else                     /* both '%' & digit (make var dis- */
  482.                     {                   /* appear).  For '%' followed by   */
  483.                     p_rawparm++;        /* non-digit, incr raw & parsed    */
  484.                     p_parsdparm++;      /* ptrs by one and continue; that  */
  485.                     counter++;          /* is, preserve original values.   */
  486.                     }
  487.                } /* END ELSE of var in range */
  488.           } /* END if (workchr == '%') */
  489.     else
  490.           {
  491.           p_rawparm++;                  /* Other 1/2 of substitution loop..*/
  492.           p_parsdparm++;                /* incr pointers & counter if      */
  493.           counter++;                    /* char just processed is not null */
  494.           }                             /* and not a variable. Continue    */
  495.      } while (workchr);                 /* looping if char was not a null. */
  496.  
  497. /*------------------------------------------------------------------------
  498.  * Command line is now built, turn it into a TOS cmdlin image for Pexec()
  499.  * display the after-substitution image.
  500.  *-----------------------------------------------------------------------*/
  501.  
  502. callparms[0] = (char)counter-1;         /* Final image done, plug in length*/
  503.  
  504. strcpy(callfile, callv[0]);             /* Copy the filename to its home.  */
  505.  
  506. say(callfile);                          /* Output file/command name.  */
  507. say("\011  ");  /* TAB char */          /* Separate command/parms */
  508. say(&callparms[1]);                     /* Output parms after substitution */
  509. say(crlf_str);                          /* Start a new line. */
  510.  
  511. if (counter > 127)            /* This is totally graceless, but might */
  512.      {                        /* help prevent crashing the machine! */
  513.      bm_msg("Command line is longer than 127 bytes after subsitution (FATAL).\r\n");
  514.      err_exit();
  515.      }
  516.  
  517. return(1);                              /* Indicate there *is* a command. */
  518. }
  519.  
  520. /*************************************************************************
  521.  *
  522.  * Add a default file extension if one doesn't exist.
  523.  *  This is a strange function.  It looks at the last four characters
  524.  *  of a string (hopefully a filename), and if it doesn't include a '.'
  525.  *  the default file extension is tacked on.
  526.  *
  527.  *************************************************************************/
  528.  
  529.  
  530. add_extension(p_newname, p_fname, p_defext)
  531. char *p_newname, *p_fname, *p_defext;
  532. {
  533. register char *p_work;
  534. register int  slen,
  535.               counter;
  536.  
  537. slen = strlen(p_fname);
  538. p_work = p_fname + slen;
  539. for (counter = 0; (counter < 4) && (counter < slen); counter++)
  540.      {
  541.      if (*p_work == '\\') /* I hate 'break' almost as much as 'goto', */
  542.           break;          /* but this handles "a:\batfiles.c\c"...    */
  543.      if (*p_work == '.')
  544.           return(0);
  545.      p_work--;
  546.      }
  547. strcat(p_newname, p_fname);
  548. strcat(p_newname, p_defext);
  549. return(0); /* why is this here? */
  550. }
  551.  
  552. /***************************************************************************
  553.  ***************************************************************************
  554.  *
  555.  * Things which are normally library routines (mine) but are included here
  556.  * to keep from confusing people who don't have my libs.  
  557.  *
  558.  **************************************************************************
  559.  **************************************************************************/
  560.  
  561. /*-------------------------------------------------------------------------
  562.  * The dLibs bzero() function is VERY broken, this takes it's place.
  563.  *------------------------------------------------------------------------*/
  564.  
  565. myzero(p_str, count)
  566. register char *p_str;
  567. register int  count;
  568. {
  569. while (count--)
  570.      *p_str++ = 0x00;
  571. }
  572.  
  573. /*-------------------------------------------------------------------------
  574.  * Check with the user to see if s/he wants to abort the batch run.
  575.  *------------------------------------------------------------------------*/
  576.  
  577. check_abort()
  578. {
  579. char workstr[4];
  580. int count;
  581.  
  582. while (Bconstat(2))       /* Suck up buffered characters before prompting. */
  583.      {
  584.      if (0x03 == (char)Bconin(2)) /* If one of the buffered characters is  */
  585.           Pterm(0);               /* a ^C, get out without prompting.      */
  586.      }                    
  587.                           
  588. do   {
  589.      say(crlf_str);
  590.      bm_msg("\033eAbort (Y/N) ? ");     /* cursor on, prompt       */
  591.      count = getstr(workstr,2,"YyNn");  /* get validated answer    */
  592.      } while (count == 0);              /* loop until valid input. */
  593.  
  594. if ((workstr[0] == 'y') || (workstr[0] == 'Y'))
  595.      Pterm(0);
  596.      
  597. say("\r\n\033f");                     /* cursor off            */
  598. }
  599.  
  600. /*-------------------------------------------------------------------------
  601.  * fixpath - Make sure a pathname ends in a '\' char or that it doesn't,
  602.  *  depending on the value of 'pathflag' (0=no trailing \, 1=trailing \).
  603.  *  If we change the string, we'll return a 1.
  604.  *-----------------------------------------------------------------------*/
  605.  
  606. fixpath(p_path, pathflag)
  607. char *p_path;
  608. int pathflag;
  609. {
  610. int pos, len;
  611.  
  612. pos = strrpos(p_path,'\\');
  613. len = strlen(p_path) -1;
  614.  
  615. if (len == -1)                /* Hack: If null string, always force'\' */
  616.      {                                          
  617.      strcat(p_path,"\\");                       
  618.      return(1);
  619.      }
  620.  
  621. if (pathflag)                 /* If the last char should be '\'... */
  622.      {
  623.      if (pos != len)          /* and it's not...                   */
  624.           {                   /* tack one on.                      */
  625.           strcat(p_path, "\\");  
  626.           return(1);
  627.           }
  628.      }
  629. else                          /* If the last char should not be '\'... */
  630.      if (pos == len)          /* and it is...                          */
  631.           {                   /* put a null over it.                   */
  632.           p_path[pos] = 0x00;
  633.           return(1);
  634.           }
  635.           
  636. return(0);                    /* If we made it to here nothing changed. */
  637. }
  638.  
  639. /*-------------------------------------------------------------------------
  640.  * Issue a message with a 'BATCHMON:' header.
  641.  *-----------------------------------------------------------------------*/
  642.  
  643. bm_msg(p_msg)
  644. char *p_msg;
  645. {
  646. say("BATCHMON: ");
  647. say(p_msg);
  648. }
  649.  
  650. /*-------------------------------------------------------------------------
  651.  * error exit.  Force the batch file closed and wait for a keystroke.
  652.  *-----------------------------------------------------------------------*/
  653.  
  654. err_exit()
  655. {
  656. getline(-1, 0L); /* Force file closed. */
  657. cmd_wait();
  658. Pterm(0);
  659. }
  660.  
  661. /*-------------------------------------------------------------------------
  662.  * Force a string to uppercase.  Modifies string in-place.
  663.  *-----------------------------------------------------------------------*/
  664.  
  665. upcase(p_string)
  666. register char *p_string;
  667. {
  668. do   {
  669.      if ((*p_string >= 'a') && (*p_string <= 'z'))
  670.           *p_string -= 32;
  671.      } while (*++p_string);
  672. }
  673.  
  674. /*-------------------------------------------------------------------------
  675.  * Some character I/O routines which use BIOS calls, because if you've
  676.  * got TurboST installed the DOS calls are too buggy.
  677.  *-----------------------------------------------------------------------*/
  678.  
  679. say(p_str)
  680. register char *p_str;
  681. {
  682. while (*p_str)
  683.      Bconout(2,*p_str++);
  684. }
  685.  
  686. sayint(intnum)
  687. int  intnum;
  688. {
  689. char work[20];
  690.  
  691. int2asc(work,intnum); /* integer to ascii (becomes itoa() under Sozobon) */
  692. say(work);
  693. }
  694.  
  695. /***************************************************************************
  696.  *
  697.  * getstr - Get edited string
  698.  *
  699.  *   Usage:
  700.  *
  701.  *        integer = getstr(str_to_fill,int_maxlen,str_of_valid_chars);
  702.  *
  703.  *   Example:
  704.  *
  705.  *        int  length,getstr();
  706.  *        char string[20];
  707.  *        static string ok_chars[] = {"0123456789.-"};
  708.  *        
  709.  *        length = getstr(string,20,ok_chars);
  710.  *
  711.  *   Return:
  712.  *
  713.  *        length contains length of null-terminated string (not including
  714.  *               the null itself).
  715.  *        string contains the string entered.
  716.  *
  717.  *   Externals:
  718.  *
  719.  *        None.
  720.  *
  721.  *   Notes:
  722.  *   
  723.  *        The only thing this routine has over Cconrs() is the validity
  724.  *        checking of valid_chars on a character-by-character basis...
  725.  *        if the user hits a key that's not valid, it won't be echoed
  726.  *        to the screen, cluing him (hopefully) that it's a bad char.
  727.  *
  728.  *        If the length of valid_chars is 0 (null string), all keyboard
  729.  *        chars are considered valid. 
  730.  *
  731.  *        The maximum characters the user can enter is maxlen-1, insuring
  732.  *        room to add the null terminator to the string.  DON'T PASS THIS
  733.  *        ROUTINE A LENGTH OF ONE UNLESS YOU EXPECT TO RECEIVE A NULL
  734.  *        STRING! (A length of 1 has the effect of making this a very
  735.  *        fancy wait-for-carriage-return routine!).
  736.  
  737.  *        The following keys allow the user to edit the input, making it
  738.  *        impossible to return the ASCII value of these keys.  These keys
  739.  *        are ALWAYS valid, regardless of the content of valid_chars:
  740.  *
  741.  *             <RETURN> - Used to terminate input
  742.  *             <DELETE> - Backspace and delete input chars
  743.  *             <ESC>    - Clear input string and start over
  744.  *
  745.  *        Note that no attempt is made to deal with the cursor keys for 
  746.  *        editing input -- they will be returned as data.
  747.  *
  748.  **************************************************************************/
  749.  
  750. int  getstr(string,maxlen,valid_chars)
  751. int  maxlen;
  752. char string[],valid_chars[];
  753. {
  754.  
  755. int  done, inctr, ichar, all_valid, index();
  756. char wchar;
  757.  
  758. all_valid = ! valid_chars[0];
  759. inctr     = 0;
  760. done      = 0;
  761. maxlen--;
  762.  
  763. while (!done)
  764.      {
  765.      while (!Bconstat(2));    /*do nothing until key is hit*/
  766.      wchar = ichar = Bconin(2) & 0x00FF;
  767.  
  768.      switch(ichar)
  769.           {
  770.           case 0x000D: /* RETURN ley */
  771.  
  772.  
  773.                string[inctr] = 0x00;
  774.                Bconout(2,10);Bconout(2,13);
  775.                done++;
  776.                break;
  777.  
  778.           case 0x0008: /* DELETE key */
  779.  
  780.                if (inctr)
  781.                     {
  782.                     Bconout(2,8); Bconout(2,32); Bconout(2,8);
  783.                     inctr--;
  784.                     }
  785.                break;
  786.  
  787.           case 0x001B: /* ESC key */
  788.  
  789.                while (inctr)
  790.                     {
  791.                     Bconout(2,8); Bconout(2,32); Bconout(2,8);
  792.                     inctr--;
  793.                     }
  794.                break;
  795.  
  796.           default:     /* any other key */
  797.  
  798.                if ((inctr < maxlen) && (all_valid || (instr(valid_chars,wchar))))
  799.                     {
  800.                     string[inctr++] = wchar;
  801.                     Bconout(2,ichar);
  802.                     }
  803.                break;
  804.           }
  805.      }
  806. return(inctr);
  807. }
  808.  
  809. /* Return 1/0 indicating char is in string, or not. subfunction of getstr()*/
  810.  
  811. instr(str,chr)
  812. register char *str,chr;
  813. {
  814. while(*str)
  815.      if (chr == *str++)
  816.           return(1);
  817. return(0);
  818. }
  819.  
  820. /**************************************************************************
  821.  *
  822.  * bldargv --  Scan a string, setting pointers to the space-delimited
  823.  *             words within it.  Null terminate each word except the
  824.  *             last (which should already be null terminated, anyway).
  825.  *             Strip leading spaces from the args (that is...point
  826.  *             past them).
  827.  *
  828.  *   Usage:
  829.  *        Pass this routine a pointer to an array which will hold
  830.  *        the pointers to the arguments, a pointer to the string buffer,
  831.  *        and the maximum number of pointers your array can hold. It
  832.  *        will return the number of pointers generated.  Note that if
  833.  *        there are more words in the buffer than you have pointer slots
  834.  *        for, the last pointer will be to the remainder of the buffer.
  835.  *
  836.  *        Confused?  Check this out:
  837.  *
  838.  *             char string[] = "    c:\test.prg   arga   argb   argc"
  839.  *             char *argv[3];
  840.  *             numargs = bldargv(argv,string,3);
  841.  *
  842.  *        This sequence will yield the following:
  843.  *
  844.  *             numargs = 3
  845.  *             argv[0] -> "c:\test.prg"
  846.  *             argv[1] -> "arga"
  847.  *             argv[2] -> "argb   argc"
  848.  *
  849.  *        Note that the leading spaces were removed from the arguments,
  850.  *        but the imbedded spaces within the 'rest of the buffer' 3rd
  851.  *        arg were left intact.
  852.  *
  853.  * Maintenance:
  854.  *  04/13/87 - Original version. 
  855.  *
  856.  **************************************************************************/
  857.  
  858. bldargv(argv,p_strbuf,maxarg)
  859. register char *argv[],*p_strbuf;
  860. int  maxarg;
  861. {
  862. register int  argc;
  863.  
  864. argc = 0;
  865.  
  866. while(*p_strbuf)
  867.      {
  868.      while (*p_strbuf == ' ')      /* Cruise through spaces */
  869.           p_strbuf++;
  870.      if (*p_strbuf)                /* Found non-space, but skip if null. */
  871.           {
  872.           argv[argc++] = p_strbuf; /* Found start of word, mark its loc. */
  873.           if (argc == maxarg)
  874.                return(argc);       /* If out of pointer slots, return.   */
  875.           while (*p_strbuf != ' ') /* Cruise through word (non-blank).   */
  876.                {
  877.                if (*p_strbuf == 0x00)   /* If end of string, return.     */
  878.                     return(argc);
  879.                p_strbuf++;              /* Else look at next char.       */
  880.                }
  881.           *p_strbuf++ = 0x00;      /* Found end of word, null-term word. */
  882.           }
  883.      }
  884. return(argc); /* If string ended on a run of spaces we'll exit this way. */
  885. }
  886.  
  887. /***************************************************************************
  888.  *
  889.  * GetLine --  Routine to read a CRLF-terminated line from a file, & 
  890.  *             return it as a NULL-terminated string in your buffer.
  891.  *             A null in the file will hose things but good.
  892.  * Usage:
  893.  *        On the first call, function must be non-zero and indicates 
  894.  *        your maximum buffer size for subsequent calls, and p_out is
  895.  *        a pointer to a file name to process.  This call sets things
  896.  *        up, but doesn't process any actual data. A negative return
  897.  *        from this call is an Fopen() error.  A positive return is the
  898.  *        file handle, but don't fuck with it.  If your buffer is
  899.  *        completely filled (maximum size is returned), you'll get the
  900.  *        rest of the line on the next call.  This can be  good or bad...
  901.  *
  902.  *        On following calls, function is zero, and p_out is a pointer
  903.  *        to a buffer big enough to hold a line.  Big enough is up to
  904.  *        you - there is no kind of overflow checking. A negative return
  905.  *        from this call is an Fread() error, or -128, indicatng EOF;
  906.  *        the file will be closed.  A positive return (including zero) 
  907.  *        indicates the number of characters moved into your buffer.
  908.  *        Zero indicates a line of just CRLF in the file, so you get
  909.  *        an empty (NULL char only) buffer.
  910.  *
  911.  * Maintenance:
  912.  *
  913.  * 04/12/87 -  Original version.
  914.  * 04/13/87 -  Added: If function is negative, it will force a close
  915.  *             of the currently open file.
  916.  *
  917.  **************************************************************************/
  918.  
  919. getline(function,p_out)
  920. int  function;
  921. register char *p_out;
  922. {
  923. static   char buffer[512],*p_buffer;
  924. static   int  charcntr,fhandle,maxlength;
  925. register int  outlength;
  926.  
  927. if (function)
  928.     {
  929.     if ((function < 0) && (fhandle > 0))
  930.           {
  931.           Fclose(fhandle);
  932.           fhandle = 0;
  933.           }
  934.      else
  935.           {
  936.           charcntr = 0;
  937.           maxlength = function;
  938.           return((fhandle = Fopen(p_out,0)));
  939.           }
  940.      }
  941.  
  942. *p_out = outlength = 0;
  943. while(1)
  944.      {
  945.      if (!charcntr)
  946.           {
  947.           if (!(charcntr = Fread(fhandle,512L,buffer)))
  948.                {
  949.                if (outlength)
  950.                     {
  951.                     *p_out = 0x00;
  952.                     return(outlength);
  953.                     }
  954.                else
  955.                     {
  956.                     Fclose(fhandle);
  957.                     fhandle = 0;
  958.                     return(-128);
  959.                     }
  960.                }
  961.           p_buffer = buffer;
  962.           }
  963.      charcntr--;
  964.      if ('\r' == *p_buffer)
  965.           {
  966.           *p_out = 0x00;
  967.           p_buffer++;
  968.           return(outlength);
  969.           }
  970.      if ('\n' == *p_buffer)
  971.           p_buffer++;
  972.      else
  973.           {
  974.           if(++outlength == maxlength)
  975.                {
  976.                *p_out = 0x00;
  977.                return(maxlength);
  978.                }
  979.           *p_out++ = *p_buffer++;
  980.           }
  981.      }
  982. }
  983.  
  984.  
  985.